package cn.com.generaldata.os.service.impl;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.PostConstruct;

import org.nutz.castor.Castors;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.impl.sql.SqlTemplate;
import org.nutz.dao.sql.Criteria;
import org.nutz.lang.Mirror;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.com.generaldata.jsme_indicator_system.entity.BiIndicatorNz;
import cn.com.generaldata.jsme_indicator_system.entity.BiShowNz;
import cn.com.generaldata.jsme_indicator_system.entity.ReportTypeIndicator;
import cn.com.generaldata.os.dao.intf.BaseDAOInterface;
import cn.com.generaldata.os.pojo.Report;
import cn.com.generaldata.os.pojo.ReportDetail;
import cn.com.generaldata.os.util.SpringContextHolder;

import com.google.common.collect.ImmutableMap;

/**
 * 渔业水产品指标数据管理服务类.
 */
@Service
public class BIShowDataService {

	private static Logger logger = LoggerFactory.getLogger(BIShowDataService.class);

	public enum InterceptorEvent {
		SAVE, DELETE, UPDATE;
	}

	@Autowired
	private Dao dao;

	@Autowired
	private SqlTemplate sqlTemplate;

	private final Mirror<BiShowNz> fpdMirror = Mirror.me(BiShowNz.class);

	private final Mirror<ReportDetail> rdMirror = Mirror.me(ReportDetail.class);

	private ExecutorService executorService;

	@PostConstruct
	public void init() {
		executorService = Executors.newFixedThreadPool(getBestPoolSize());
	}

	/**
	 * 根据 Java 虚拟机可用处理器数目返回最佳的线程数。<br>
	 * 最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)，其中阻塞系数这里设为0.9
	 */
	private static int getBestPoolSize() {
		try {
			// JVM可用处理器的个数
			final int cores = Runtime.getRuntime().availableProcessors();
			// 最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)
			// TODO 阻塞系数是不是需要有个setter方法能让使用者自由设置呢？
			return (int) (cores / (1 - 0.9));
		} catch (Throwable e) {
			// 异常发生时姑且返回10个任务线程池
			return 10;
		}
	}

	/**
	 * 使用报表详情信息更新，渔业水产品数据表.
	 * 
	 * @param reportDetail 报表数据详情
	 * @param event 拦截器事件
	 */
	public void changeData(final ReportDetail reportDetail, final InterceptorEvent event) {

		executorService.execute(new Runnable() {
			@Override
			public void run() {
				try {

					BaseDAOInterface<Report> reportDao = SpringContextHolder.getBean("baseDAO");
					// 根据报表查看是否需要进行数据更新处理
					if (reportDetail.getReport() == null)
						return;
					Report report = reportDao.get(Report.class, reportDetail.getReport().getId());
					//					Report report = reportDetail.getReport();
					if (report == null)
						return;
					// 查询该报表是否和指标详情有关联
					List<BiIndicatorNz> fps = getBiIndicators(report.getReportTypeId(), reportDetail);
					if (fps.isEmpty()) {
						return;
					} else {
						applyReportDetailChange(report, reportDetail, fps, event);
					}

				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}
			}
		});

	}

	/**
	 * 应该报表数据详情的更改.
	 * 
	 * @param report 报表
	 * @param reportDetail 报表数据
	 * @param fps 报表相关联的水产品品种
	 * @param event 拦截器事件
	 */
	public void applyReportDetailChange(Report report, ReportDetail reportDetail, List<BiIndicatorNz> fps,
			InterceptorEvent event) {
		for (BiIndicatorNz fp : fps) {
			fisheryProductDataChange(report, reportDetail, fp, event);
		}
	}

	/**
	 * 水产品数据变更.
	 * 
	 * @param report 报表
	 * @param reportDetail
	 * @param fp 水产品事件
	 * @param event 拦截器事件
	 */
	private void fisheryProductDataChange(Report report, ReportDetail reportDetail, BiIndicatorNz fp,
			InterceptorEvent event) {
		Criteria cri = getFisheryProductDataCnd(report, fp);
		if (InterceptorEvent.DELETE == event) {
			dao.clear(BiShowNz.class, cri);
		} else {
			BiShowNz fpd = dao.fetch(BiShowNz.class, cri);
			if (fpd == null) {
				addFisheryProductData(report, reportDetail, fp);
			} else {
				updateFisheryProductData(report, reportDetail, fp, fpd);
			}
		}

	}

	/**
	 * 更新渔业水产品指标数据.
	 * 
	 * @param report
	 * @param reportDetail
	 * @param fp
	 * @param fpd
	 */
	private void updateFisheryProductData(Report report, ReportDetail reportDetail, BiIndicatorNz fp, BiShowNz fpd) {
		List<ReportTypeIndicator> rtis = getReportTypeIndicator(report, reportDetail, fp);
		fpd = setFisheryProductDataAttrVal(fpd, reportDetail, rtis);
		if (fpd != null)
			saveOrUpdateFisheryProductData(fpd);
	}

	/**
	 * 增加渔业水产品指标数据.
	 * 
	 * @param report 报表基础数据
	 * @param reportDetail 报表数据详情
	 * @param fp 水产品品种
	 */
	private void addFisheryProductData(Report report, ReportDetail reportDetail, BiIndicatorNz fp) {
		BiShowNz fpd = new BiShowNz();

		fpd.setAreaId(report.getArea().getId());
		fpd.setUnitId(report.getUnitId());
		fpd.setIndicatorId(fp.getIndicatorId());
		fpd.setYear(Integer.valueOf(report.getReportYear()));
		//月报
		if ("0".equals(report.getReportTimetype()))
			fpd.setMonth(report.getMonth());
		//是季报
		if ("2".equals(report.getReportTimetype()))
			fpd.setQuarter(report.getQuarter());

		fpd.setYearType(Integer.valueOf(report.getReportYearTime()));

		List<ReportTypeIndicator> rtis = getReportTypeIndicator(report, reportDetail, fp);
		fpd = setFisheryProductDataAttrVal(fpd, reportDetail, rtis);
		if (fpd != null)
			saveOrUpdateFisheryProductData(fpd);
	}

	/**
	 * 保存或更新水产品指标数据
	 * 
	 * @param fpd
	 */
	private void saveOrUpdateFisheryProductData(BiShowNz fpd) {

		if (fpd.getId() == null) {
			dao.insert(fpd);
		} else {
			dao.update(fpd);
		}
	}

	/**
	 * 设置水产品的单位面积产量
	 * 
	 * @param areaName 养殖面积属性名称.
	 * @param fpd 水产品指标数据.
	 */
	//	private void setPerUnitOutput(String areaName, FisheryProductData fpd) {
	//		Cnd cnd = Cnd.where("fisheryProductId", "=", fpd.getFisheryProductId()).and("attrName", "=", areaName);
	//		FisheryProductIndicator fpi = dao.fetch(FisheryProductIndicator.class, cnd);
	//		if (fpi != null) {
	//			Double area = (Double) fpdMirror.getValue(fpd, areaName);
	//			Indicator indicator = dao.fetch(Indicator.class, fpi.getIndicatorId());
	//			// 将公顷转换为亩
	//			if ("公顷".equals(indicator.getUnit())) {
	//				area = area * 15;
	//			}
	//
	//			String perUnitOutputStr = String.format("%.2f", fpd.getOutput() / area);
	//			fpd.setPerUnitOutput(new BigDecimal(perUnitOutputStr));
	//		}
	//	}

	/**
	 * 设置报表数据到水产品指标数据中.
	 * 
	 * @param reportDetail 报表数据
	 * @param rtis 报表指标关系
	 */
	private BiShowNz setFisheryProductDataAttrVal(BiShowNz fpd, ReportDetail reportDetail,
			List<ReportTypeIndicator> rtis) {
		int i = 0;
		for (ReportTypeIndicator rti : rtis) {
			if (fpd.getIndicatorId().equals(rti.getIndicatorId())) {
				//				Cnd cnd = Cnd.where("indicatorId", "=", rti.getIndicatorId());
				//				BiIndicatorNz fpi = dao.fetch(BiIndicatorNz.class, cnd);
				//				if (fpi != null) {
				try {
					Field field = fpdMirror.getField("indicatorValue");
					Object value = getIndicatorValFromReportDeatil(rti, reportDetail, field.getType());
					fpdMirror.setValue(fpd, field, value);
					break;
				} catch (Exception e) {
					System.out.println("-------------------指标异常：" + rti.getIndicatorId());
				}
			}
			i++;
		}
		if (i == rtis.size())
			return null;
		return fpd;
	}

	/**
	 * 根据指标关系从报表数据中取得指标值
	 * 
	 * @param rti 报表指标关系
	 * @param reportDetail 报表数据
	 * @param targetType 指标值类型
	 * @return 转化后指标值
	 */
	private <T> T getIndicatorValFromReportDeatil(ReportTypeIndicator rti, ReportDetail reportDetail,
			Class<T> targetType) throws Exception {
		Object val = rdMirror.getValue(reportDetail, "f" + rti.getTheColumn());
		return Castors.me().castTo(val, targetType);
	}

	/**
	 * 获得水产品关联报表指标指标关系
	 * 
	 * @param report 报表基础信息
	 * @param reportDetail 报表数据
	 * @param fp 水产品
	 * @return 该水产品关联的报表指标关系
	 */
	private List<ReportTypeIndicator> getReportTypeIndicator(Report report, ReportDetail reportDetail, BiIndicatorNz fp) {
		String sql = "SELECT rti.* FROM t_report_type_indicator rti "
				+ "WHERE rti.row_index = @rowIndex AND rti.indicator_id = @indicatorId AND rti.report_type_id = @reportTypeId";
		Map<String, Object> params = ImmutableMap.<String, Object> of("rowIndex", reportDetail.getRowIndex(),
				"indicatorId", fp.getIndicatorId(), "reportTypeId", report.getReportTypeId());

		return sqlTemplate.query(sql, params, ReportTypeIndicator.class);
	}

	private Criteria getFisheryProductDataCnd(Report report, BiIndicatorNz fp) {
		Criteria cri = Cnd.cri();
		cri.where().and("unitId", "=", report.getUnitId());
		cri.where().and("indicatorId", "=", fp.getIndicatorId());
		cri.where().and("year", "=", Integer.valueOf(report.getReportYear()));
		//月报
		if ("0".equals(report.getReportTimetype()))
			cri.where().and("month", "=", report.getMonth());
		//是季报
		if ("2".equals(report.getReportTimetype()))
			cri.where().and("quarter", "=", report.getQuarter());
		return cri;
	}

	/**
	 * 获得报表相关联的BI查询指标
	 * 
	 * @param reportTypeId 报表模板id
	 * @return
	 */
	public List<BiIndicatorNz> getBiIndicators(String reportTypeId, ReportDetail detail) {
		List<BiIndicatorNz> fps = null;

		String sql = "SELECT fpi.* FROM tbl_bi_indicator fpi INNER JOIN t_report_type_indicator rti ON fpi.indicator_id = rti.indicator_id WHERE rti.report_type_id = @reportTypeId and rti.row_index=@rowIndex";

		Map params = ImmutableMap.of("reportTypeId", reportTypeId, "rowIndex", detail.getRowIndex());
		fps = sqlTemplate.query(sql, params, BiIndicatorNz.class);
		return fps;
	}
}
